﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using metroCIS.Enums.Library;
using metroCIS.Library.Enums;
using metroCIS.ViewModel;
using Windows.Web.Syndication;

namespace metroCIS.Library
{
    public class CisManager
    {
        private readonly string _username;
        private readonly string _pwd;
        
        private const string LvPlanOverview = LvPlanPrivate + "stpl_week.php";
        private const string LvPlanPrivate = "http://cis.technikum-wien.at/cis/private/lvplan/";
        private const string LoginUrl = "http://cis.technikum-wien.at/cis/menu.php?content_id=173";
        private const string GradeUrl = "http://cis.technikum-wien.at/cis/private/lehre/notenliste.php";
        private const string RoomPlanUrl = "http://cis.technikum-wien.at/cis/private/lvplan/stpl_week.php?type=ort&ort_kurzbz=";
        private const string FreeRoomsUrl = "https://cis.technikum-wien.at/cis/infoterminal/index.php?raumtyp_kurzbz=";
        private const string RssUrl = "http://www.technikum-wien.at/cms/rss.php";
        private const string LVOverviewInfoUrl = "https://cis.technikum-wien.at/cis/menu.php?content_id=173";
        private const string LVInfoUrl = "https://cis.technikum-wien.at/cis/";
        private const string LvDetailInfoUrl = "https://cis.technikum-wien.at/cis/private/lehre/ects/preview.php?lv=";
        private const string RoomAvailabilityUrl = "https://cis.technikum-wien.at/cis/infoterminal/index.php?raumtyp_kurzbz=";

        private HttpClient _client;

        public CisManager(string username, string pwd)
        {
            _username = username;
            _pwd = pwd;
        }

        public static CisManager Create()
        {
            CisManager man =  new CisManager("wi11b091","CHRistian0408");
            man.Login();
            return man;
        }

        public async Task<bool> Login()
        {
            try
            {
                _client = new HttpClient();
                var auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(_username + ":" + _pwd));
                _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", auth);
                var html = await _client.GetStringAsync(LoginUrl);
                return true;
            }
            catch
            {
                return false;
            }
        }
        
        public async Task<IEnumerable<LvEntry>> GetLVPlan()
        {
            if(_client == null)
                throw new Exception("Please first login ... !");
            return await ParseLvEntries(LvPlanOverview);
        }

        public async Task<IEnumerable<string>> GetFreeRooms(RoomType roomType)
        {
            if (_client == null)
                throw new Exception("Please first login ... !");
            var res = await _client.GetStringAsync(FreeRoomsUrl + roomType);
            var rooms = Regex.Matches(res, "<span class=\"(.*?)", RegexOptions.Multiline);
            return null;
        }

        public async Task<IEnumerable<LvEntry>> GetRoomPlan(string roomName)
        {
            if(_client == null)
                throw new Exception("Please first login ... !");
            return await ParseLvEntries(RoomPlanUrl + roomName);
        }

        private async Task<List<LvEntry>> ParseLvEntries(string url)
        {
            var lvPlan = await _client.GetStringAsync(url);
            var calUrl = Regex.Match(lvPlan, "\"" + "(.*?)" + "&format=csv");
            if (calUrl == null)
                return null;
            var csv = await _client.GetStringAsync(LvPlanPrivate + calUrl.Value.TrimStart('"'));
            var res = new List<LvEntry>();
            using (var reader = new StringReader(csv))
            {
                reader.ReadLine(); //First 2 Lines - Header skip it!

                while (reader.Peek() > 0)
                {
                    try
                    {
                        var entry = new LvEntry();
                        var line = reader.ReadLine();
                        var parts = line.Split(',').Select(x => x.Trim('"')).ToList();
                        entry.Title = parts[0]; //First line contains LV

                        reader.ReadLine();
                        entry.Lector = reader.ReadLine();
                        reader.ReadLine(); //Not required

                        line = reader.ReadLine();
                        parts = line.Split(',').Select(x => x.Trim('"')).ToList();
                        entry.Room = parts[0];
                        entry.From = DateTime.Parse(parts[2]).Add(TimeSpan.Parse(parts[3]));
                        entry.To = DateTime.Parse(parts[4]).Add(TimeSpan.Parse(parts[5]));
                        res.Add(entry);
                    }
                    catch (Exception ex)
                    {

                    }
                }
                return res;
            }
        }

        public async Task<IEnumerable<Grade>> GetGrades()
        {
            if (_client == null)
                throw new Exception("Please first login ... !");

            List<Grade> gradeList = new List<Grade>();

            for (int i = DateTime.Today.Year; i > 2000; i--)
            {
                var ws = await GetGradeForSemester("WS" + i);
                gradeList.AddRange(ws);
                var ss = await GetGradeForSemester("SS" + i);
                gradeList.AddRange(ss);

                if (ws.Count() == 0 && ss.Count() == 0)
                {
                    return gradeList;
                }
            }

            return gradeList;
        }

        private async Task<IEnumerable<Grade>> GetGradeForSemester(string sem)
        {
            if (_client == null)
                throw new Exception("Please first login ... !");

            var grades = await _client.GetStringAsync(GradeUrl + "?stsem=" + sem);
            var start = grades.IndexOf("<table>");
            var res = new List<Grade>();
            if (start != -1)
            {
                start += 7;
                var end = grades.LastIndexOf("</table>") - start;
                grades = grades.Substring(start, end);
                grades = Regex.Replace(grades, "(<[^>]*>)", ";");
                var parts = grades.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Skip(4).ToList();
                
                for (int i = 0; i < parts.Count; i += 4)
                {
                    res.Add(new Grade()
                    {
                        LV = parts[i],
                        LVGrade = parts[i + 1],
                        CertificateGrade = parts[i + 2],
                        DateOfGrade = DateTime.Parse(parts[i + 3]),
                        Semester = sem
                    });
                }
            }

            return res;
        }

        public async Task<List<RssItem>> GetRssFeed()
        {
            SyndicationFeed feed = new SyndicationFeed();
            HttpClient http = new System.Net.Http.HttpClient();
            HttpResponseMessage response = await http.GetAsync(RssUrl);
            feed.Load(await response.Content.ReadAsStringAsync());

            List<RssItem> rssItems = new List<RssItem>();

            foreach (SyndicationItem item in feed.Items)
            {
                RssItem rssItem = new RssItem()
                {
                    Title = WebUtility.HtmlDecode(item.Title.Text).Trim(),
                    Description = WebUtility.HtmlDecode(item.Summary.Text),
                    Link = item.Links[0].Uri,
                    Date = item.PublishedDate.LocalDateTime
                };

                rssItems.Add(rssItem);

            }

            return rssItems;
        }

        public async Task<List<LVInfo>> GetLvInfo()
        {
            if (_client == null)
                throw new Exception("Please first login ... !");

            List<LVInfo> lvInfoList = new List<LVInfo>();
            var meinCisContent = await _client.GetStringAsync(LVOverviewInfoUrl);

            var searchStringLv = "<a class=\"Item2\" title=\"";
            var searchStringLvLink = "private/lehre/lesson.php?lvid=";
            var searchStringLektorMail = "<a class=\"Item2\" href=\"mailto:";
            var searchStringMoodle = "https://cis.technikum-wien.at/moodle/course/view.php?id=";
            var lvStart = meinCisContent.IndexOf(searchStringLv);
            var lvEnd = 0;
            var title = "";
            var lvLink = "";
            var moodleLink = "";
            var currentLV = 0;
            Uri moodleUri;
            String lektorMail;
            String lektorName;

            var lvContent = "";

            if (lvStart != -1)
            {
                while (lvStart != -1)
                {
                    //Find LV title
                    lvStart = lvStart + searchStringLv.Length;
                    lvEnd = meinCisContent.IndexOf("\"", lvStart);
                    title = meinCisContent.Substring(lvStart, lvEnd - lvStart);

                    //find LV link for detail information
                    lvStart = meinCisContent.IndexOf(searchStringLvLink, lvStart);
                    lvStart = lvStart + searchStringLvLink.Length;
                    lvEnd = meinCisContent.IndexOf("\"", lvStart);
                    currentLV = lvEnd;
                    lvLink = meinCisContent.Substring(lvStart, lvEnd - lvStart);

                    //get detail informatio string
                    lvContent = await _client.GetStringAsync(LVInfoUrl + searchStringLvLink + lvLink);

                    //get name and mail of lektor
                    lvStart = lvContent.IndexOf(searchStringLektorMail);
                    lvStart = lvStart + searchStringLektorMail.Length;
                    lvEnd = lvContent.IndexOf("\"", lvStart);
                    lektorMail = lvContent.Substring(lvStart, lvEnd - lvStart);

                    lvStart = lvContent.IndexOf(">", lvEnd);
                    lvStart = lvStart + 1;
                    lvEnd = lvContent.IndexOf("<", lvStart);
                    lektorName = lvContent.Substring(lvStart, lvEnd - lvStart); 

                    //Find moodle link
                    lvStart = lvContent.IndexOf(searchStringMoodle);
                    if (lvStart != -1)
                    {
                        lvStart = lvStart + searchStringMoodle.Length;
                        lvEnd = lvContent.IndexOf("\"", lvStart);
                        moodleLink = lvContent.Substring(lvStart, lvEnd - lvStart);
                        moodleUri = new Uri(searchStringMoodle + moodleLink); 
                    }
                    else
                    {
                        moodleUri = null;
                    }
                    
                    LVInfo lvInfo = new LVInfo()
                    {
                        Title = title,
                        Moodle = moodleUri,
                        LVInforation = new Uri(LvDetailInfoUrl + lvLink),
                        Lektor = lektorName,
                        LektorMail = lektorMail
                    };
                    lvInfoList.Add(lvInfo);

                    lvStart = meinCisContent.IndexOf(searchStringLv, currentLV);
                }
            }

            return lvInfoList;
        }

        public async Task<List<RoomItem>> GetRooms()
        {
            if (_client == null)
                throw new Exception("Please first login ... !");

            _client = new HttpClient();

            List<RoomItem> rooms = new List<RoomItem>();
            String content, name;

            int start, end;

            String searchStringFree = "gruen_mitteText\">";
            String searchStringCurrentlyFree = "orange_mitteText\">";
            String searchStringOccupied = "rot_mitteText\">";

            foreach (RoomType item in Enum.GetValues(typeof(RoomType)))
            {
                content = await _client.GetStringAsync(RoomAvailabilityUrl+item.ToString());

                start = content.IndexOf(searchStringFree);

                if (start != -1)
                {
                    while (start != -1)
                    {
                        start = start + searchStringFree.Length;
                        end = content.IndexOf("\n", start);
                        name = content.Substring(start, end - start);

                        if (name.Length <= 10)
                        {
                            rooms.Add(new RoomItem()
                            {
                                Name = name,
                                RoomType = item,
                                RoomAvailability = RoomAvailability.free
                            });
                        }

                        start = content.IndexOf(searchStringFree, start);
                    }
                }

                start = content.IndexOf(searchStringCurrentlyFree);

                if (start != -1)
                {
                    while (start != -1)
                    {
                        start = start + searchStringCurrentlyFree.Length;
                        end = content.IndexOf("\n", start);
                        name = content.Substring(start, end - start);

                        if (name.Length <= 10)
                        {
                            rooms.Add(new RoomItem()
                            {
                                Name = name,
                                RoomType = item,
                                RoomAvailability = RoomAvailability.currentlyFree
                            });
                        }

                        start = content.IndexOf(searchStringCurrentlyFree, start);
                    }
                }

                start = content.IndexOf(searchStringOccupied);

                if (start != -1)
                {
                    while (start != -1)
                    {
                        start = start + searchStringOccupied.Length;
                        end = content.IndexOf("\n", start);
                        name = content.Substring(start, end - start);

                        if (name.Length <= 10)
                        {
                            rooms.Add(new RoomItem()
                            {
                                Name = name,
                                RoomType = item,
                                RoomAvailability = RoomAvailability.occupied
                            });
                        }

                        start = content.IndexOf(searchStringOccupied, start);
                    }
                }
            }

            return rooms;
        }
    }
}
